Osvojte si soubory deklarací TypeScriptu (.d.ts) a odemkněte typovou bezpečnost a automatické dokončování pro jakoukoli knihovnu JavaScriptu. Naučte se používat @types, vytvářet vlastní definice a zacházet s kódem třetích stran jako profík.
Odemknutí ekosystému JavaScriptu: Hluboký ponor do souborů deklarací TypeScriptu
TypeScript způsobil revoluci v moderním vývoji webových aplikací tím, že přinesl statické typování do dynamického světa JavaScriptu. Tato typová bezpečnost poskytuje neuvěřitelné výhody: zachycení chyb v době kompilace, umožnění výkonného automatického dokončování v editoru a výrazné zlepšení údržby velkých kódových základen. Hlavní výzva však nastává, když chceme používat rozsáhlý ekosystém existujících knihoven JavaScriptu – z nichž většina nebyla napsána v TypeScriptu. Jak náš striktně typovaný kód TypeScriptu rozumí tvarům, funkcím a proměnným z netypované knihovny JavaScriptu?
Odpověď spočívá v souborech deklarací TypeScriptu. Tyto soubory, identifikovatelné příponou .d.ts, jsou základním mostem mezi světy TypeScriptu a JavaScriptu. Fungují jako plán nebo smlouva API, popisující typy knihovny třetí strany bez toho, aby obsahovaly jakoukoli z jejích skutečných implementací. V této komplexní příručce prozkoumáme vše, co potřebujete vědět, abyste mohli s jistotou spravovat definice typů pro jakoukoli knihovnu JavaScriptu ve vašich projektech TypeScriptu.
Co přesně jsou soubory deklarací TypeScriptu?
Představte si, že jste si najali dodavatele, který mluví pouze jiným jazykem. Abyste s ním mohli efektivně pracovat, potřebovali byste překladatele nebo podrobný soubor instrukcí v jazyce, kterému oba rozumíte. Deklarační soubor slouží pro kompilátor TypeScriptu (dodavatele) přesně k tomuto účelu.
Soubor .d.ts obsahuje pouze informace o typech. Zahrnuje:
- Signatury pro funkce a metody (typy parametrů, návratové typy).
- Definice pro proměnné a jejich typy.
- Rozhraní a typové aliasy pro složité objekty.
- Definice tříd, včetně jejich vlastností a metod.
- Struktury jmenných prostorů a modulů.
Zásadní je, že tyto soubory neobsahují žádný spustitelný kód. Jsou určeny pouze pro statickou analýzu. Když importujete knihovnu JavaScriptu, jako je Lodash, do svého projektu TypeScriptu, kompilátor hledá odpovídající deklarační soubor. Pokud jej najde, může ověřit váš kód, poskytnout inteligentní automatické dokončování a zajistit, že knihovnu používáte správně. Pokud jej nenajde, zobrazí chybu jako: Could not find a declaration file for module 'lodash'.
Proč jsou soubory deklarací pro profesionální vývoj nepostradatelné
Používání knihoven JavaScriptu bez správných definic typů v projektu TypeScriptu podkopává samotný důvod používání TypeScriptu. Zvažme jednoduchý scénář pomocí populární pomocné knihovny Lodash.
Svět bez definic typů
Bez deklaračního souboru TypeScript vůbec netuší, co lodash je nebo co obsahuje. Abyste vůbec dosáhli kompilace kódu, můžete být v pokušení použít rychlou opravu, jako je tato:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Automatické dokončování? Žádná pomoc.
// Kontrola typu? Ne. Je 'username' správná vlastnost?
// Kompilátor to umožňuje, ale může to selhat za běhu.
_.find(users, { username: 'fred' });
V tomto případě je proměnná _ typu any. To efektivně říká TypeScriptu: „Nekontroluj nic, co se týká této proměnné.“ Ztratíte všechny výhody: žádné automatické dokončování, žádná kontrola typu argumentů a žádná jistota ohledně návratového typu. To je živná půda pro chyby za běhu.
Svět s definicemi typů
Nyní se podívejme, co se stane, když poskytneme potřebný deklarační soubor. Po instalaci typů (které si probereme dále) se zážitek transformuje:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Editor poskytuje automatické dokončování pro 'find' a další funkce lodash.
// 2. Najetí myší na 'find' zobrazí jeho plnou signaturu a dokumentaci.
// 3. TypeScript vidí, že `users` je pole objektů `User`.
// 4. TypeScript ví, že predikát pro `find` na `User[]` by měl zahrnovat `user` nebo `active`.
// SPRÁVNĚ: TypeScript je spokojený.
const fred = _.find(users, { user: 'fred' });
// CHYBA: TypeScript zachytí chybu!
// Property 'username' does not exist on type 'User'.
const betty = _.find(users, { username: 'betty' });
Rozdíl je jako den a noc. Získáme plnou typovou bezpečnost, vynikající vývojářskou zkušenost prostřednictvím nástrojů a dramatické snížení potenciálních chyb. To je profesionální standard pro práci s TypeScriptem.
Hierarchie hledání definic typů
Jak tedy získáte tyto magické soubory .d.ts pro své oblíbené knihovny? Existuje dobře zavedený proces, který pokrývá drtivou většinu scénářů.
Krok 1: Zkontrolujte, zda knihovna obsahuje vlastní typy
Nejlepší scénář nastane, když je knihovna napsána v TypeScriptu nebo její správci poskytují oficiální deklarační soubory ve stejném balíčku. To je stále běžnější u moderních, dobře udržovaných projektů.
Jak zkontrolovat:
- Nainstalujte knihovnu obvyklým způsobem:
npm install axios - Podívejte se do složky knihovny v
node_modules/axios. Vidíte nějaké soubory.d.ts? - Zkontrolujte soubor
package.jsonknihovny, zda neobsahuje pole"types"nebo"typings". Toto pole ukazuje přímo na hlavní deklarační soubor. Napříkladpackage.jsonAxiosa obsahuje:"types": "index.d.ts".
Pokud jsou tyto podmínky splněny, máte hotovo! TypeScript automaticky najde a použije tyto svázané typy. Není potřeba žádná další akce.
Krok 2: Projekt DefinitelyTyped (@types)
Pro tisíce knihoven JavaScriptu, které neobsahují vlastní typy, vytvořila globální komunita TypeScriptu neuvěřitelný zdroj: DefinitelyTyped.
DefinitelyTyped je centralizované, komunitou spravované úložiště na GitHubu, které hostuje vysoce kvalitní deklarační soubory pro obrovské množství balíčků JavaScriptu. Tyto definice jsou publikovány do registru npm v rozsahu @types.
Jak jej používat:
Pokud knihovna, jako je lodash, neobsahuje vlastní typy, jednoduše nainstalujete odpovídající balíček @types jako vývojovou závislost:
npm install --save-dev @types/lodash
Konvence pojmenování je jednoduchá a předvídatelná: pro balíček s názvem package-name budou jeho typy téměř vždy na @types/package-name. Dostupné typy můžete vyhledat na webu npm nebo přímo v úložišti DefinitelyTyped.
Proč --save-dev? Deklarační soubory jsou potřeba pouze během vývoje a kompilace. Neobsahují žádný kód za běhu, takže by neměly být zahrnuty do vašeho konečného produkčního balíčku. Instalace jako devDependency zajišťuje toto oddělení.
Krok 3: Když neexistují žádné typy – napište si vlastní
Co když používáte starší, specializovanou nebo interní soukromou knihovnu, která neobsahuje typy a není na DefinitelyTyped? V tomto případě si musíte vyhrnout rukávy a vytvořit si vlastní deklarační soubor. I když to může znít zastrašující, můžete začít jednoduše a podle potřeby přidávat další podrobnosti.
Rychlá oprava: Zjednodušená ambientní deklarace modulu
Někdy prostě potřebujete, aby se váš projekt zkompiloval bez chyb, zatímco vymyslíte správnou strategii typování. Můžete vytvořit soubor ve svém projektu (např. declarations.d.ts nebo types/global.d.ts) a přidat zjednodušenou deklaraci:
// in a .d.ts file
declare module 'some-untyped-library';
To říká TypeScriptu: „Věř mi, modul s názvem 'some-untyped-library' existuje. Jen zacházej se vším importovaným z něj jako s typem any.“ To umlčí chybu kompilátoru, ale jak jsme diskutovali, obětuje to veškerou typovou bezpečnost pro danou knihovnu. Je to dočasná záplata, nikoli dlouhodobé řešení.
Vytvoření základního vlastního deklaračního souboru
Lepší přístup je začít definovat typy pro části knihovny, které skutečně používáte. Řekněme, že máme jednoduchou knihovnu s názvem `string-utils`, která exportuje jedinou funkci.
// In node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
Můžeme vytvořit soubor string-utils.d.ts ve vyhrazeném adresáři `types` v kořenovém adresáři našeho projektu.
// In my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// You could add other function definitions here as you use them
// export function slugify(str: string): string;
}
Nyní musíme říct TypeScriptu, kde má hledat naše vlastní definice typů. Uděláme to v tsconfig.json:
{
"compilerOptions": {
// ... other options
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
S tímto nastavením, když import { capitalize } from 'string-utils', TypeScript najde váš vlastní deklarační soubor a poskytne typovou bezpečnost, kterou jste definovali. Tento soubor můžete postupně rozšiřovat, jak budete používat další funkce knihovny.
Ponoření hlouběji: Vytváření deklaračních souborů
Prozkoumejme některé pokročilejší koncepty, se kterými se setkáte při psaní nebo čtení deklaračních souborů.
Deklarování různých druhů exportů
Moduly JavaScriptu mohou exportovat věci různými způsoby. Váš deklarační soubor se musí shodovat s exportní strukturou knihovny.
- Pojmenované exporty: Toto je nejběžnější. Viděli jsme to výše s `export function capitalize(...)`. Můžete také exportovat konstanty, rozhraní a třídy.
- Výchozí export: Pro knihovny, které používají `export default`.
- UMD Globals: Pro starší knihovny určené k práci v prohlížečích prostřednictvím tagu
<script>se často připojují ke globálnímu objektu `window`. Můžete deklarovat tyto globální proměnné. - `export =` a `import = require()`: Tato syntaxe je pro starší moduly CommonJS, které používají `module.exports = ...`. Například pokud knihovna dělá `module.exports = myClass;`.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// For a function default export
export default function myCoolFunction(): void;
// For an object default export
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Declares a global variable '$' of a certain type
declare var $: JQueryStatic;
// in my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// in your app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
I když je to u moderních ES modulů méně běžné, je to zásadní pro kompatibilitu s mnoha staršími, ale stále široce používanými balíčky Node.js.
Rozšíření modulu: Rozšíření existujících typů
Jednou z nejvýkonnějších funkcí je rozšíření modulu (známé také jako slučování deklarací). To vám umožňuje přidávat vlastnosti do existujících rozhraní definovaných v deklaračním souboru jiného balíčku. To je nesmírně užitečné pro knihovny s architekturou pluginů, jako jsou Express nebo Fastify.
Představte si, že používáte middleware v Expressu, který přidává vlastnost `user` do objektu `Request`. Bez rozšíření by si TypeScript stěžoval, že `user` na `Request` neexistuje.
Zde je návod, jak můžete TypeScriptu říct o této nové vlastnosti:
// in your types/express.d.ts file
// We must import the original type to augment it
import { UserProfile } from './auth'; // Assuming you have a UserProfile type
// Tell TypeScript we're augmenting the 'express-serve-static-core' module
declare module 'express-serve-static-core' {
// Target the 'Request' interface inside that module
interface Request {
// Add our custom property
user?: UserProfile;
}
}
Nyní bude v celé vaší aplikaci objekt Express `Request` správně typován s volitelnou vlastností `user` a získáte plnou typovou bezpečnost a automatické dokončování.
Směrnice se třemi lomítky
Někdy můžete vidět komentáře v horní části souborů .d.ts, které začínají třemi lomítky (///). Jedná se o směrnice se třemi lomítky, které fungují jako instrukce kompilátoru.
/// <reference types="..." />: Toto je nejběžnější. Explicitně zahrnuje definice typů jiného balíčku jako závislost. Například typy pro plugin WebdriverIO mohou zahrnovat/// <reference types="webdriverio" />, protože jeho vlastní typy závisí na základních typech WebdriverIO./// <reference path="..." />: Používá se k deklaraci závislosti na jiném souboru ve stejném projektu. Jedná se o starší syntaxi, která byla z velké části nahrazena importy modulů ES.
Doporučené postupy pro správu deklaračních souborů
- Preferujte svázané typy: Při výběru mezi knihovnami upřednostňujte ty, které jsou napsány v TypeScriptu nebo obsahují vlastní oficiální definice typů. Signalizuje to závazek k ekosystému TypeScriptu.
- Udržujte
@typesvdevDependencies: Vždy instalujte balíčky@typespomocí--save-devnebo-D. Nejsou potřeba pro váš produkční kód. - Slaďte verze: Běžným zdrojem chyb je neshoda mezi verzí knihovny a její verzí
@types. Hlavní navýšení verze v knihovně (např. z v2 na v3) bude pravděpodobně mít zásadní změny v jejím API, které se musí odrazit v balíčku@types. Snažte se je udržovat synchronizované. - Použijte
tsconfig.jsonpro kontrolu: Možnosti kompilátorutypeRootsatypesve vašemtsconfig.jsonvám mohou poskytnout podrobnou kontrolu nad tím, kde TypeScript hledá deklarační soubory.typeRootsříká kompilátoru, které složky má zkontrolovat (ve výchozím nastavení je to./node_modules/@types) atypesvám umožňuje explicitně vypsat, které balíčky typů zahrnout. - Přispějte zpět: Pokud napíšete komplexní deklarační soubor pro knihovnu, která jej nemá, zvažte jeho přispění do projektu DefinitelyTyped. Je to fantastický způsob, jak vrátit něco globální vývojářské komunitě a pomoci tisícům dalších.
Závěr: Neopěvovaní hrdinové typové bezpečnosti
Soubory deklarací TypeScriptu jsou neopěvovaní hrdinové, kteří umožňují bezproblémovou integraci dynamického, rozsáhlého světa JavaScriptu do robustního vývojového prostředí s typovou bezpečností. Jsou to zásadní spojení, která posilují naše nástroje, zabraňují nesčetným chybám a činí naše kódové základny odolnějšími a samosprávnějšími.
Tím, že pochopíte, jak najít, používat a dokonce vytvářet vlastní soubory .d.ts, neopravujete pouze chybu kompilátoru – zvyšujete celý svůj vývojový pracovní postup. Odemknete plný potenciál TypeScriptu i bohatého ekosystému knihoven JavaScriptu, čímž vytvoříte silnou synergii, která vede k lepšímu a spolehlivějšímu softwaru pro globální publikum.